#include "cef_c_wrapper.h"

bool EnableHighDpiSupport() {
    // 尝试Windows 10+ API
    typedef enum {
        PROCESS_DPI_UNAWARE = 0,
        PROCESS_SYSTEM_DPI_AWARE = 1,
        PROCESS_PER_MONITOR_DPI_AWARE = 2
    } PROCESS_DPI_AWARENESS;

    typedef HRESULT(WINAPI * SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS);

    HMODULE hShcore = GetModuleHandleW(L"shcore.dll");
    if (hShcore) {
        SetProcessDpiAwarenessPtr pSetProcessDpiAwareness =
            (SetProcessDpiAwarenessPtr)GetProcAddress(hShcore, "SetProcessDpiAwareness");
        if (pSetProcessDpiAwareness) {
            return SUCCEEDED(pSetProcessDpiAwareness((PROCESS_DPI_AWARENESS)2));
        }
    }

    // 尝试Windows 8.1 API
    typedef BOOL(WINAPI * SetProcessDpiAwarenessContextPtr)(DWORD);
    HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
    if (hUser32) {
        SetProcessDpiAwarenessContextPtr pSetProcessDpiAwarenessContext =
            (SetProcessDpiAwarenessContextPtr)GetProcAddress(hUser32, "SetProcessDpiAwarenessContext");
        if (pSetProcessDpiAwarenessContext) {
            // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 0x0000000D
            return pSetProcessDpiAwarenessContext(0x0000000D);
        }
    }

    // 尝试Windows 8 API
    typedef BOOL(WINAPI * SetProcessDpiAwarePtr)();
    SetProcessDpiAwarePtr pSetProcessDpiAware = (SetProcessDpiAwarePtr)GetProcAddress(hUser32, "SetProcessDpiAware");
    if (pSetProcessDpiAware) {
        return pSetProcessDpiAware();
    }

    return false;
}

int hash_api() {
    const char *hash = cef_api_hash(CEF_API_VERSION, 0);
    LOG_DEBUG("hash: %s", hash);
    if (strcmp(hash, CEF_API_HASH_PLATFORM)) {
        LOG_ERROR("API versions do not match: %s != %s", hash, CEF_API_HASH_PLATFORM);
        return 0;
    }
    return 1;
}

// 初始设置 --------------------------------------
void *init_main_args(int argc, char *argv[]) {
    cef_main_args_t *main_args = (cef_main_args_t *)calloc(1, sizeof(cef_main_args_t));
    if (argc == 1) {
        LOG_INFO("init_main_args argc = %d", argc);
    } else {
        for (int i = 1; i < argc; i++) {
            if (strlen(argv[i]) > 128)
                LOG_DEBUG("... ");
            else
                LOG_DEBUG("%s ", argv[i]);
        }
    }
    main_args->instance = GetModuleHandle(NULL);
    LOG_DEBUG("info", "cef_c_wrapper", "init_main_args", "end");
    return main_args;
}

cef_settings_t *init_settings_t() {
    cef_settings_t *settings = (cef_settings_t *)calloc(1, sizeof(cef_settings_t));
    settings->size = sizeof(cef_settings_t);
    settings->log_severity = LOGSEVERITY_VERBOSE;
    LOG_DEBUG("cef_settings_t size %d", sizeof(cef_settings_t));
    return settings;
}

cef_browser_settings_t *init_browser_settings() {
    cef_browser_settings_t *settings = (cef_browser_settings_t *)calloc(1, sizeof(cef_browser_settings_t));
    settings->size = sizeof(cef_browser_settings_t);
    return settings;
}

// cef --函数 --------------------------------------
int execute_process(void *args, cef_app_t *application, void *windows_sandbox_info) {
    cef_main_args_t *aargs = (cef_main_args_t *)args;
    int exit_code = cef_execute_process(args, application, windows_sandbox_info);
    LOG_DEBUG("exit_code = %d", exit_code);
    return exit_code;
}

int initialize_cef(void *args, struct _cef_settings_t *settings, cef_app_t *application, void *windows_sandbox_info) {
    LOG_DEBUG("cef_settings_t size = %d", settings->size);
    LOG_DEBUG("cef_app_t size = %d", application->base.size);
    cef_main_args_t *aargs = (cef_main_args_t *)args;
    return cef_initialize(aargs, settings, application, windows_sandbox_info);
}

void run_message_loop() { cef_run_message_loop(); }

void quit_message_loop() { cef_quit_message_loop(); }

void shutdown_cef() {
    // 清理函数注册
    hashmap_free(ipcRenderer_handler_map);
    hashmap_free(ipcRenderer_callback_map);
    // 清理全局http 协议注册
    free(g_http_scheme_factory);
    LOG_DEBUG("log: shutdown_cef...................\n");
    cef_shutdown();
    LOG_DEBUG("log: shutdown_cef2...................\n");
}

// cef_string --函数------------------------------------------------------------
int string_utf8_to_utf16(const char *src, size_t src_len, cef_string_utf16_t *output) {
    return cef_string_utf8_to_utf16(src, src_len, output);
}
int string_utf16_to_utf8(const char16_t *src, size_t src_len, cef_string_utf8_t *output) {
    return cef_string_utf16_to_utf8(src, src_len, output);
}

void string_userfree_utf8_free(cef_string_userfree_utf8_t str) { cef_string_userfree_utf8_free(str); }
void string_userfree_utf16_free(cef_string_userfree_utf16_t str) { cef_string_userfree_utf16_free(str); }

// app -------------------------------------------------------------------
browser_app *browser_app_create() { return browser_app_create_t(); }

cef_app_t *get_browser_app(browser_app *app) { return &app->cef_app; }

browser_view_delegate *create_browser_view_delegate(int popup_width, int popup_height, char *popup_icon_png_path,
                                                    char *popup_title, int is_popup_frameless) {
    return create_browser_view_delegate_t(popup_width, popup_height, popup_icon_png_path, popup_title,
                                          is_popup_frameless);
}

// browser_view------------------------------------------------------------
cef_browser_view_t *browser_view_create(page_handler *page, char *url, const struct _cef_browser_settings_t *settings,
                                        struct _cef_dictionary_value_t *extra_info,
                                        struct _cef_request_context_t *request_context,
                                        browser_view_delegate *delegate) {
    cef_string_t cef_url = {};

    // 检测是否是本地文件，并注册协议
    if (strncmp(url, "http", 4) != 0) {
        g_http_scheme_factory = create_http_scheme_factory();
        register_scheme_http(g_http_scheme_factory);

        // 替换url
        const char *prefix = "http://local_file/";
        size_t length = strlen(prefix) + strlen(url) + 1;
        char *new_url = (char *)malloc(length);
        if (new_url == NULL) {
            // 内存分配失败
            LOG_ERROR("load_file new_url Memory allocation failed\n");
            return NULL;
        }
        sprintf(new_url, "%s%s", prefix, url);
        cef_string_utf8_to_utf16(new_url, strlen(new_url), &cef_url);
        free(new_url);
    } else {
        cef_string_utf8_to_utf16(url, strlen(url), &cef_url);
    }

    cef_browser_view_t *browser_view = cef_browser_view_create(&page->cef_client, &cef_url, settings, extra_info,
                                                               request_context, &delegate->cef_browser_view_delegate);
    browser_view->base.base.add_ref(
        &browser_view->base
             .base); // 防止提前释放， 解决调用浏览器对象时，对象被提前释放问题， 发送renderer消息时，对象被提前释放问题
    cef_string_clear(&cef_url);
    return browser_view;
}

/**
 * 注册http协议，用于加载本地html和js,css等
 */
int register_scheme_http(cef_scheme_handler_factory_t *factory) {
    // 注册协议
    // cef_scheme_handler_factory_t *factory = create_http_scheme_factory();
    cef_string_t scheme_name = {};
    cef_string_t domain_name = {};
    cef_string_utf8_to_utf16("http", strlen("http"), &scheme_name);
    cef_string_utf8_to_utf16("local_file", strlen("local_file"), &domain_name);
    // printf("log:  cef_register_scheme_handler_factory start11----------------\n");
    int result = cef_register_scheme_handler_factory(&scheme_name, &domain_name, factory);

    cef_string_utf16_clear(&scheme_name);
    cef_string_utf16_clear(&domain_name);
    return result;
}

cef_scheme_handler_factory_t *create_http_scheme_factory() { return create_http_scheme_factory_t(); }

// 加载url
void load_url(cef_browser_view_t *browser_view, char *url) { browser_load_url(browser_view, url); }

/**
 * 加载本地html,
 * 路径相对于exe文件所在目录
 * 使用前需要调用register_scheme_http注册协议
 */
void load_file(cef_browser_view_t *browser_view, const char *html) { browser_load_file(browser_view, html); }

page_handler *create_page_handler() { return create_page_handler_t(); }

// window---------------------------------------------------------------
window_delegate *create_window_delegate(cef_browser_view_t *browser_view, int width, int heigth, char *icon_png_path,
                                        char *title, int is_frameless, int is_devtools) {
    return _create_window_delegate(browser_view, width, heigth, icon_png_path, title, is_frameless, is_devtools);
}
cef_window_t *window_create_top_level(struct _cef_window_delegate_t *delegate) {
    return cef_window_create_top_level(delegate);
}

cef_window_t *create_window(window_delegate *delegate) {
    return cef_window_create_top_level(&(delegate->cef_window_delegate));
}

void set_window_icon_png(cef_window_t *window, const char *file_path) { set_icon_png(window, file_path); }
void set_window_position(cef_window_t *window, int x, int y) { set_position(window, x, y); }
void set_window_title(cef_window_t *window, const char *title) { set_title(window, title); }
void set_window_child_view(cef_window_t *window, cef_browser_view_t *browser_view) {
    window->base.add_child_view(&window->base, &browser_view->base);
}

// command_line --------------------------------------------------------------------------
cef_command_line_t *command_line_create() { return command_line_create_t(); }
void command_line_init(cef_command_line_t *command_line, int argc, char *argv[]) {
    command_line_init_t(command_line, argc, argv);
}
int command_line_process_type(cef_command_line_t *command_line) { return hash_process(command_line); }

// renderer------------------------------------------------------------------------------------
renderer_app *renderer_app_create() { return renderer_app_create_t(); }

cef_app_t *get_renderer_app(renderer_app *app) { return &app->cef_app; }

// other app -------------------------------------------------------------------------------
other_app *other_app_create() { return other_app_create_t(); }

cef_app_t *get_other_app(browser_app *app) { return &app->cef_app; }

// IPC---------------------------------------------------------------------------------------------------

void *create_ipc_value_int(int value) { return _create_ipc_value_int(value); }
void *create_ipc_value_double(double value) { return _create_ipc_value_double(value); }
void *create_ipc_value_bool(int value) { return _create_ipc_value_bool(value); }
void *create_ipc_value_string(char *value) { return _create_ipc_value_string(value); }
void *create_ipc_value_binary(char *data, int size) { return _create_ipc_value_binary(data, size); }
void* create_ipc_value_list() {
    return _create_ipc_value_list();
}
void* ipc_value_list_add_element(IPC_Value* list, IPC_Value* element) {
    return _list_add_element(list, element);
}
int ipc_value_get_element_count(IPC_Value *value) {
    return ipc_value_get_count(value);
}
void* ipc_value_get_element(IPC_Value *value, int index) {
    return _ipc_value_get_element(value, index);
}


int ipc_value_get_int(void *ipc_value) { return _ipc_value_get_int((IPC_Value *)ipc_value); }
double ipc_value_get_double(void *ipc_value) { return _ipc_value_get_double((IPC_Value *)ipc_value); }
char *ipc_value_get_string(void *ipc_value) { return _ipc_value_get_string((IPC_Value *)ipc_value); }
int ipc_value_get_bool(void *ipc_value) { return _ipc_value_get_bool((IPC_Value *)ipc_value); }

IPC_Value_Type ipc_value_get_type(void *ipc_value) { return _ipc_value_get_type((IPC_Value *)ipc_value); }

void ipc_main_send(cef_browser_view_t *browser_view, const char *event_name, void *value, int request_id) {
    IPC_Value *ipc_value = (IPC_Value *)value;
    cef_browser_t *browser = browser_view->get_browser(browser_view);
    _ipc_main_send(browser, event_name, ipc_value, request_id);
}

void set_main_process_message_handeler(on_main_process_message_handler on_handler) {
    _set_main_process_message_handeler(on_handler);
}

// 设置日志级别--------------------------------------------------------------------------------------------------
void set_log_level(LogLevel level) { _set_log_level(level); }

// ---------------------平台相关-------------------------------------------------------------------------------
CEF_C_WRAPPER_EXPORT int EmbedManifest(const char *executablePath, const char *manifestPath, int resourceId) {
    return _EmbedManifest(executablePath, manifestPath, resourceId);
}